package com.chris.code.service;

import cn.hutool.core.io.FileUtil;
import com.chris.code.model.Field;
import com.chris.code.model.GenerateParams;
import com.chris.code.model.Table;
import com.chris.code.util.FieldNameUtil;
import freemarker.cache.ClassTemplateLoader;
import freemarker.cache.NullCacheStorage;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateExceptionHandler;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.*;
import java.sql.Types;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Copyright (c) 2018-2025, chris All rights reserved.
 * <p>
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * <p>
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the pig4cloud.com developer nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * <p>
 * Author: haoka (haokang207@126.com)
 * <p>
 * Date: 2018/9/30 16:29
 **/
@Service
public class CodeService {

    public static String camelCaseName(String underscoreName) {
        StringBuilder result = new StringBuilder();
        if (underscoreName != null && underscoreName.length() > 0) {
            boolean flag = false;
            for (int i = 0; i < underscoreName.length(); i++) {
                char ch = underscoreName.charAt(i);
                if ("_".charAt(0) == ch) {
                    flag = true;
                } else {
                    if (flag) {
                        result.append(Character.toUpperCase(ch));
                        flag = false;
                    } else {
                        result.append(ch);
                    }
                }
            }
        }
        return result.toString();
    }

    public void generate(GenerateParams params, Table table, List<Field> fields) throws IOException, TemplateException {
        String name = params.getName();
        String packageName = params.getPackageName();
        String description = params.getDescription();
        String author = params.getAuthor();
        Map<String, Object> dataMap = new HashMap<>();
        String lowerName = name.substring(0, 1).toLowerCase() + name.substring(1);
        name = lowerName.substring(0, 1).toUpperCase() + lowerName.substring(1);
        String primary = table.getPrimary();
        primary = StringUtils.isEmpty(primary) ? "id" : primary;
        String primaryFieldName = FieldNameUtil.underlineToCamel(primary);
        String primaryFieldNameUpper = primaryFieldName.substring(0, 1).toUpperCase() + primaryFieldName.substring(1);
        primaryFieldName = primaryFieldName.substring(0, 1).toLowerCase() + primaryFieldName.substring(1);
        String primaryFieldType = null;
        for (Field field : fields) {
            field.setTypeName(sqlType2JavaType(field.getType()));
            if (field.getRefType() > 0) {
                field.setTypeName(sqlType2JavaType(field.getRefType()));
            }
            if (field.getName().equals(primary)) {
                primaryFieldType = field.getTypeName();
            }
        }
        dataMap.put("name", name);
        dataMap.put("appName", params.getAppName());
        dataMap.put("tableName", params.getTableName());
        dataMap.put("packageName", packageName);
        dataMap.put("lowerName", lowerName);
        dataMap.put("author", author);
        dataMap.put("description", description);
        dataMap.put("time", LocalDateTime.now().atOffset(ZoneOffset.ofHours(8)).format(DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm")));
        dataMap.put("primaryFieldName", primaryFieldName);
        dataMap.put("primaryFieldNameUpper", primaryFieldNameUpper);
        dataMap.put("primaryFieldType", primaryFieldType);
        for (Field field : fields) {
            //下划线转驼峰
            field.setName(camelCaseName(field.getName()));
        }
        dataMap.put("fields", fields);
        Configuration configuration = new Configuration(Configuration.VERSION_2_3_22);
        String basePackagePath = "/templates";
        configuration.setTemplateLoader(new ClassTemplateLoader(CodeService.class, basePackagePath));
        configuration.setDefaultEncoding("UTF-8");
        configuration.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
        configuration.setCacheStorage(NullCacheStorage.INSTANCE);
        File basePackage = new File(CodeService.class.getClassLoader().getResource("").getPath(), basePackagePath);
        List<File> files = FileUtil.loopFiles(basePackage);
        //清理工作空间
        File workDir = new File("code");
        if (workDir.exists()) {
            workDir.delete();
            workDir.mkdir();
        }
        for (File file : files) {
            String filePath = file.getPath();
            String subPath = filePath.substring(basePackage.getPath().length());
            String toPath = "code" + subPath.replaceAll("_NAME_", name).replaceAll("_PACKAGE_NAME_", packageName)
                    .replaceAll("_LOWERNAME_", lowerName).replace(".ftl", "");
            Template template = configuration.getTemplate(subPath);
            generateControllerFile(template, toPath, dataMap);
        }
    }

    private void generateControllerFile(Template template, String filePath, Map<String, Object> dataMap)
            throws IOException, TemplateException {
        File file = new File(filePath);
        file.getParentFile().mkdirs();
        file.createNewFile();
        FileOutputStream fos = new FileOutputStream(file);
        Writer out = new BufferedWriter(new OutputStreamWriter(fos, "utf-8"), 10240);
        template.process(dataMap, out);
    }

    private String sqlType2JavaType(int type) {
        switch (type) {
            case Types.VARCHAR:
                return "String";
            case Types.BIGINT:
                return "Long";
            case Types.DATE:
                return "Date";
            case Types.INTEGER:
                return "Integer";
            case Types.BINARY:
                return "String";
            case Types.BIT:
                return "Boolean";
            case Types.DOUBLE:
                return "Double";
            case Types.FLOAT:
                return "Float";
            case Types.TIMESTAMP:
                return "Date";
            case Types.TINYINT:
                return "Short";
            case Types.BOOLEAN:
                return "Boolean";
        }
        return "String";
    }
}
